package com.itcaius.weblog.web.service.Impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.itcaius.weblog.admin.event.ReadArticleEvent;
import com.itcaius.weblog.common.domain.dos.*;
import com.itcaius.weblog.common.domain.mapper.*;
import com.itcaius.weblog.common.enums.ResponseCodeEnum;
import com.itcaius.weblog.common.exception.BizException;
import com.itcaius.weblog.common.utils.PageResponse;
import com.itcaius.weblog.common.utils.Response;
import com.itcaius.weblog.web.convert.ArticleConvert;
import com.itcaius.weblog.web.markdown.MarkdownHelper;
import com.itcaius.weblog.web.model.vo.article.*;
import com.itcaius.weblog.web.model.vo.category.FindCategoryListRspVO;
import com.itcaius.weblog.web.model.vo.tag.FindTagListRspVO;
import com.itcaius.weblog.web.service.ArticleService;
import com.itcaius.weblog.web.utils.MarkdownStatusUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author Caius
 * @description
 * @since Created in 2025-04-30
 */
@Service
@Slf4j
public class ArticleServiceImpl implements ArticleService {

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private ArticleCategoryRelMapper articleCategoryRelMapper;

    @Autowired
    private TagMapper tagMapper;

    @Autowired
    private ArticleTagRelMapper articleTagRelMapper;

    @Autowired
    private ArticleContentMapper articleContentMapper;

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @Override
    public Response findArticlePageList(FindIndexArticlePageListReqVO findIndexArticlePageListReqVO) {
        Long current = findIndexArticlePageListReqVO.getCurrentPage();
        Long size = findIndexArticlePageListReqVO.getSize();

        // 获取文章主体记录
        Page<ArticleDO> articleDOPage = articleMapper.selectPage(current, size, null, null, null);

        // 获取分页数据
        List<ArticleDO> articleDOS = articleDOPage.getRecords();

        List<FindIndexArticlePageListRspVO> vos = null;
        if (!CollectionUtils.isEmpty(articleDOS)) {
            // DO 转 VO
            vos = articleDOS.stream()
                    .map(articleDO -> {
                        FindIndexArticlePageListRspVO vo = ArticleConvert.INSTANCE.convertDO2VO(articleDO);
                        vo.setIsTop(articleDO.getWeight() > 0);
                        return vo;
                    })
                    .collect(Collectors.toList());

            // 拿到所有文章合集
            List<Long> articleIds = articleDOS.stream().map(ArticleDO::getId).collect(Collectors.toList());

            //查询所有分类
            List<CategoryDO> categoryDOS = categoryMapper.selectList(Wrappers.emptyWrapper());
            // 转 Map 方便后续通过分类 ID 实现对应的分类名称
            Map<Long, String> categoryIdNameMap = categoryDOS.stream().collect(Collectors.toMap(CategoryDO::getId, CategoryDO::getName));

            // 通过文章 id 批量查询对应的 article-category 关联数据
            List<ArticleCategoryRelDO> articleCategoryRelDOS = articleCategoryRelMapper.selectByArticleIds(articleIds);

            // 设置文章分类
            vos.forEach(vo -> {
                Long currArticleId = vo.getId();
                // 筛选出当前文章的对应的关联数据
                Optional<ArticleCategoryRelDO> optional = articleCategoryRelDOS.stream().filter(rel -> Objects.equals(rel.getArticleId(), currArticleId)).findAny();

                if (optional.isPresent()) {
                    ArticleCategoryRelDO articleCategoryRelDO = optional.get();
                    Long categoryId = articleCategoryRelDO.getCategoryId();

                    String categoryName = categoryIdNameMap.get(categoryId);

                    FindCategoryListRspVO findCategoryListRspVO = FindCategoryListRspVO.builder()
                            .id(categoryId)
                            .name(categoryName)
                            .build();

                    vo.setCategory(findCategoryListRspVO);
                }

            });

            List<TagDO> tagDOS = tagMapper.selectList(Wrappers.emptyWrapper());

            Map<Long, String> tagIdNameMap = tagDOS.stream().collect(Collectors.toMap(TagDO::getId, TagDO::getName));

            List<ArticleTagRelDO> articleTagRelDOS = articleTagRelMapper.selectByArticleIds(articleIds);
            vos.forEach(vo -> {
                Long currArticleId = vo.getId();

                List<ArticleTagRelDO> articleTagRelDOList = articleTagRelDOS.stream().filter(rel -> Objects.equals(rel.getArticleId(), currArticleId)).collect(Collectors.toList());

                List<FindTagListRspVO> findTagListRspVOS = Lists.newArrayList();
                articleTagRelDOList.forEach(articleTagRelDO -> {
                    Long tagId = articleTagRelDO.getTagId();
                    String tagName = tagIdNameMap.get(tagId);

                    FindTagListRspVO findTagListRspVO = FindTagListRspVO.builder()
                            .id(tagId)
                            .name(tagName)
                            .build();
                    findTagListRspVOS.add(findTagListRspVO);
                });
                vo.setTags(findTagListRspVOS);
            });
        }
        return PageResponse.success(articleDOPage, vos);
    }

    /**
     * 获取文章详情
     * @param findArticleDetailReqVO
     * @return
     */
    @Override
    public Response findArticleDetail(FindArticleDetailReqVO findArticleDetailReqVO) {
        Long articleId = findArticleDetailReqVO.getArticleId();

        ArticleDO articleDO = articleMapper.selectById(articleId);

        if (Objects.isNull(articleDO)) {
            log.warn("==> 该文章不存在， articleId={}", articleId);
            throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND);
        }
        // 获取正文
        ArticleContentDO articleContentDO = articleContentMapper.selectByArticleId(articleId);
        String content = articleContentDO.getContent();

        // 计算 md 字数
        int totalWords = MarkdownStatusUtil.calculateWordCount(content);
        // DO 转 VO
        FindArticleDetailRspVO vo = FindArticleDetailRspVO.builder()
                .title(articleDO.getTitle())
                .createTime(articleDO.getCreateTime())
                .content(MarkdownHelper.convertMarkdown2Html(articleContentDO.getContent()))
                .readNum(articleDO.getReadNum())
                .totalWords(totalWords)
                .readTime(MarkdownStatusUtil.calculateReadingTime(totalWords))
                .build();
        // 获取分类
        ArticleCategoryRelDO articleCategoryRelDO = articleCategoryRelMapper.selectByArticleId(articleId);
        CategoryDO categoryDO = categoryMapper.selectById(articleCategoryRelDO.getCategoryId());
        vo.setCategoryId(categoryDO.getId());
        vo.setCategoryName(categoryDO.getName());

        // 获取标签
        List<ArticleTagRelDO> articleTagRelDOS = articleTagRelMapper.selectByArticleId(articleId);
        List<Long> tagIds = articleTagRelDOS.stream().map(ArticleTagRelDO::getTagId).collect(Collectors.toList());
        List<TagDO> tagDOS = tagMapper.selectByIds(tagIds);
        // DO 转 VO
        List<FindTagListRspVO> tagVOS = tagDOS.stream()
                .map(tagDO -> FindTagListRspVO.builder().id(tagDO.getId()).name(tagDO.getName()).build())
                .collect(Collectors.toList());
        vo.setTags(tagVOS);

        // 获取上一篇
        ArticleDO preArticle = articleMapper.selectPreArticle(articleId);
        if (!Objects.isNull(preArticle)) {
            FindPreNextArticleRspVO preArticleRspVO = FindPreNextArticleRspVO.builder()
                    .articleId(preArticle.getId())
                    .articleTitle(preArticle.getTitle())
                    .build();
            vo.setPreArticle(preArticleRspVO);
        }

        // 获取下一篇
        ArticleDO nextArticle = articleMapper.selectNextArticle(articleId);
        if (!Objects.isNull(nextArticle)) {
            FindPreNextArticleRspVO nextArticleRspVO = FindPreNextArticleRspVO.builder()
                    .articleId(nextArticle.getId())
                    .articleTitle(nextArticle.getTitle())
                    .build();
            vo.setNextArticle(nextArticleRspVO);
        }

        // 发布文章阅读事件
        eventPublisher.publishEvent(new ReadArticleEvent(this, articleId));

        return Response.success(vo);
    }
}
